use std::{
    collections::HashMap,
    fmt,
    fs::File,
    io::{self, BufReader, Cursor, Read, Seek},
    path::Path,
};

pub struct JsonParser<R> {
    buf_reader: BufReader<R>,
}

#[derive(Debug, PartialEq)]
pub enum JsonValue {
    Null,
    Bool(bool),
    Number(f64),
    String(String),
    Array(JsonArray),
    Object(JsonObject),
}

impl JsonValue {
    pub fn expect_null(&self) {
        match self {
            JsonValue::Null => {}
            _ => panic!("expected null"),
        }
    }

    pub fn expect_bool(&self) -> bool {
        match self {
            &JsonValue::Bool(b) => b,
            _ => panic!("expected bool"),
        }
    }

    pub fn expect_number(&self) -> f64 {
        match self {
            &JsonValue::Number(n) => n,
            _ => panic!("expected number"),
        }
    }

    pub fn expect_string(&self) -> &str {
        match self {
            JsonValue::String(s) => s,
            _ => panic!("expected string"),
        }
    }

    pub fn expect_array(&self) -> &JsonArray {
        match self {
            JsonValue::Array(a) => a,
            _ => panic!("expected array"),
        }
    }

    pub fn expect_object(&self) -> &JsonObject {
        match self {
            JsonValue::Object(o) => o,
            _ => panic!("expected object"),
        }
    }
}

#[derive(Debug, PartialEq)]
pub struct JsonArray(pub Vec<JsonValue>);

#[derive(Debug, PartialEq)]
pub struct JsonObject {
    // key can be duplicated
    pub map: HashMap<String, Vec<JsonValue>>,
}

pub enum JsonError {
    UnexpectedByte(u8),
    UnexpectedEnd,
    InvalidEscape,
    InvalidNumber,
    IoError(io::Error),
}

impl fmt::Debug for JsonError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            &JsonError::UnexpectedByte(byte) => {
                if byte.is_ascii() {
                    write!(f, "unexpected char: '{}'", byte as char)
                } else {
                    write!(f, "unexpected byte: \\x{:02x}", byte)
                }
            }
            JsonError::UnexpectedEnd => write!(f, "unexpected end"),
            JsonError::InvalidEscape => write!(f, "invalid escape"),
            JsonError::InvalidNumber => write!(f, "invalid number"),
            JsonError::IoError(err) => write!(f, "io error: {}", err),
        }
    }
}

pub type Result<T> = std::result::Result<T, JsonError>;

macro_rules! Err {
    ($err:expr) => {
        panic!("{:?}", $err)
        // Err($err)
    };
}

impl From<io::Error> for JsonError {
    fn from(err: io::Error) -> Self {
        Self::IoError(err)
    }
}

impl JsonParser<File> {
    pub fn from_file(path: impl AsRef<Path>) -> io::Result<Self> {
        let file = File::open(path)?;
        let buf_reader = BufReader::new(file);
        Ok(Self { buf_reader })
    }
}

impl<'a> JsonParser<Cursor<&'a [u8]>> {
    pub fn from_bytes(bytes: &'a [u8]) -> io::Result<Self> {
        let buf_reader = BufReader::new(Cursor::new(bytes));
        Ok(Self { buf_reader })
    }
}

impl<R: Read + Seek> JsonParser<R> {
    pub fn parse(&mut self) -> Result<JsonObject> {
        self.expect_object()
    }

    fn try_eat_non_white(&mut self) -> Result<Option<u8>> {
        for byte in self.buf_reader.by_ref().bytes() {
            let byte = byte?;
            if byte.is_ascii_whitespace() {
                continue;
            }
            return Ok(Some(byte));
        }
        Ok(None)
    }

    fn try_eat(&mut self) -> Result<Option<u8>> {
        for byte in self.buf_reader.by_ref().bytes() {
            return Ok(Some(byte?));
        }
        Ok(None)
    }

    fn eat(&mut self) -> Result<u8> {
        for byte in self.buf_reader.by_ref().bytes() {
            return Ok(byte?);
        }
        Err!(JsonError::UnexpectedEnd)
    }

    fn eat_non_white(&mut self) -> Result<u8> {
        for byte in self.buf_reader.by_ref().bytes() {
            let byte = byte?;
            if byte.is_ascii_whitespace() {
                continue;
            }
            return Ok(byte);
        }
        Err!(JsonError::UnexpectedEnd)
    }

    fn peek_non_white(&mut self) -> Result<u8> {
        for byte in self.buf_reader.by_ref().bytes() {
            let byte = byte?;
            if byte.is_ascii_whitespace() {
                continue;
            }
            self.buf_reader.seek_relative(-1)?;
            return Ok(byte);
        }
        Err!(JsonError::UnexpectedEnd)
    }

    fn expect_byte(&mut self, expected: u8) -> Result<()> {
        let byte = self.peek_non_white()?;
        if byte != expected {
            return Err!(JsonError::UnexpectedByte(byte));
        }
        self.eat_non_white()?;
        Ok(())
    }

    fn expect_bytes(&mut self, expected: &[u8]) -> Result<()> {
        self.peek_non_white()?;
        let mut len = 0;
        for (byte, &expected) in self.buf_reader.by_ref().bytes().zip(expected) {
            let byte = byte?;
            if byte != expected {
                return Err!(JsonError::UnexpectedByte(byte));
            }
            len += 1;
        }
        if len < expected.len() {
            return Err!(JsonError::UnexpectedEnd);
        }
        Ok(())
    }

    fn expect_value(&mut self) -> Result<JsonValue> {
        match self.peek_non_white()? {
            b'n' => self.expect_null(),
            b't' | b'f' => self.expect_bool().map(JsonValue::Bool),
            b'0'..=b'9' | b'-' => self.expect_number().map(JsonValue::Number),
            b'"' => self.expect_string().map(JsonValue::String),
            b'[' => self.expect_array().map(JsonValue::Array),
            b'{' => self.expect_object().map(JsonValue::Object),
            byte => Err!(JsonError::UnexpectedByte(byte)),
        }
    }

    fn expect_object(&mut self) -> Result<JsonObject> {
        self.expect_byte(b'{')?;
        self.eat_object()
    }

    fn eat_object(&mut self) -> Result<JsonObject> {
        let mut map = HashMap::new();
        loop {
            match self.eat_non_white()? {
                b'}' => return Ok(JsonObject { map }),
                b',' => continue,
                _ => {
                    self.buf_reader.seek_relative(-1)?;
                    let key = self.expect_string()?;
                    self.expect_byte(b':')?; // eat ':'
                    let value = self.expect_value()?;
                    map.entry(key).or_default().push(value);
                }
            }
        }
    }

    fn expect_string(&mut self) -> Result<String> {
        self.expect_byte(b'"')?;
        self.eat_string()
    }

    fn eat_string(&mut self) -> Result<String> {
        let mut string = Vec::new();
        loop {
            match self.eat()? {
                b'"' => return Ok(String::from_utf8_lossy(&string).into_owned()),
                b'\\' => {
                    string.push(self.eat_escape()?);
                }
                byte => string.push(byte),
            }
        }
    }

    fn eat_escape(&mut self) -> Result<u8> {
        match self.eat()? {
            b'"' => Ok(b'"'),
            b'\\' => Ok(b'\\'),
            b'b' => Ok(b'\x08'),
            b'f' => Ok(b'\x0c'),
            b'n' => Ok(b'\n'),
            b'r' => Ok(b'\r'),
            b't' => Ok(b'\t'),
            // b'u' => self.eat_unicode(),
            byte => Err!(JsonError::InvalidEscape),
        }
    }

    fn expect_null(&mut self) -> Result<JsonValue> {
        self.expect_bytes(b"null")?;
        Ok(JsonValue::Null)
    }

    fn expect_bool(&mut self) -> Result<bool> {
        match self.peek_non_white()? {
            b't' => {
                self.expect_bytes(b"true")?;
                Ok(true)
            }
            b'f' => {
                self.expect_bytes(b"false")?;
                Ok(false)
            }
            byte => Err!(JsonError::UnexpectedByte(byte)),
        }
    }

    fn expect_number(&mut self) -> Result<f64> {
        let mut number = Vec::new();
        loop {
            match self.try_eat()? {
                Some(byte @ (b'0'..=b'9' | b'.' | b'e' | b'-')) => number.push(byte),
                byte => {
                    if byte.is_some() {
                        self.buf_reader.seek_relative(-1)?;
                    }
                    return String::from_utf8_lossy(&number)
                        .parse()
                        .map_err(|_| JsonError::InvalidNumber);
                }
            }
        }
    }

    fn expect_array(&mut self) -> Result<JsonArray> {
        self.expect_byte(b'[')?;
        self.eat_array()
    }

    fn eat_array(&mut self) -> Result<JsonArray> {
        let mut array = Vec::new();
        loop {
            match self.eat_non_white()? {
                b']' => return Ok(JsonArray(array)),
                b',' => continue,
                _ => {
                    self.buf_reader.seek_relative(-1)?;
                    array.push(self.expect_value()?);
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_eat_non_white() {
        let json = r#"{"key1": "value1", "key2": 2, "key3": true, "key4": null}"#;
        let mut parser = JsonParser::from_bytes(json.as_bytes()).unwrap();
        for b in json.bytes().filter(|&b| !b.is_ascii_whitespace()) {
            assert_eq!(parser.eat_non_white().unwrap(), b);
        }
    }

    #[test]
    fn test_string() {
        let json = r#""key1""#;
        let mut parser = JsonParser::from_bytes(json.as_bytes()).unwrap();
        assert_eq!(parser.expect_string().unwrap(), "key1");
    }

    #[test]
    fn test_number() {
        let json = r#"2"#;
        let mut parser = JsonParser::from_bytes(json.as_bytes()).unwrap();
        assert_eq!(parser.expect_number().unwrap(), 2.0);
    }

    #[test]
    fn test_bool() {
        let json = r#"true"#;
        let mut parser = JsonParser::from_bytes(json.as_bytes()).unwrap();
        assert_eq!(parser.expect_bool().unwrap(), true);
    }

    #[test]
    fn test_null() {
        let json = r#"null"#;
        let mut parser = JsonParser::from_bytes(json.as_bytes()).unwrap();
        assert_eq!(parser.expect_value().unwrap(), JsonValue::Null);
    }

    #[test]
    fn test_json_object() {
        let json = r#"{"key1": "value1", "key2": 2, "key3": true, "key4": null, "key5": []}"#;
        let object = JsonParser::from_bytes(json.as_bytes())
            .unwrap()
            .parse()
            .unwrap();
        assert_eq!(object.map.len(), 5);
        assert_eq!(
            object.map["key1"],
            vec![JsonValue::String("value1".to_string())]
        );
        assert_eq!(object.map["key2"], vec![JsonValue::Number(2.0)]);
        assert_eq!(object.map["key3"], vec![JsonValue::Bool(true)]);
        assert_eq!(object.map["key4"], vec![JsonValue::Null]);
        assert_eq!(
            object.map["key5"],
            vec![JsonValue::Array(JsonArray(vec![]))]
        );
    }

    #[test]
    fn test_json_object_unicode() {
        let json = r#"{ "成都": ["宜宾", "自贡", "绵阳", "泸州"] }"#;
        let object = JsonParser::from_bytes(json.as_bytes())
            .unwrap()
            .parse()
            .unwrap();
        assert_eq!(object.map.len(), 1);
        assert_eq!(
            object.map["成都"],
            vec![JsonValue::Array(JsonArray(vec![
                JsonValue::String("宜宾".to_string()),
                JsonValue::String("自贡".to_string()),
                JsonValue::String("绵阳".to_string()),
                JsonValue::String("泸州".to_string()),
            ]))]
        );
    }
}
